package it.geosolutions.geoserver.rest.http;

import java.io.IOException;
import net.sf.json.JSONObject;

import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.NameValuePair;

public class KeycloakAuthenticator implements GeoServerRestAuthenticator {
    
    private String username;
    
    private String pw;
    
    private String clientId;
    
    private String clientSecret;
    
    private String url;
    
    private String realm;
    
    private String accessToken;
    
    private long accessTokenExpired;
    
    private String refreshToken;
    
    private long refreshTokenExpired;
    
    private String proxyHost;
    
    private int proxyPort;
        
    public KeycloakAuthenticator(String username, String pw, String clientId, String clientSecret,
            String url, String realm, String proxyHost, int proxyPort) {
        this.username = username;
        this.pw = pw;
        this.clientId = clientId;
        this.clientSecret = clientSecret;
        this.url = url;
        this.realm = realm;
        this.proxyHost = proxyHost;
        this.proxyPort = proxyPort;
    }

    private synchronized void getNewToken(HttpClient client) throws IOException {
        long currentTime = System.currentTimeMillis() / 1000;
        if (currentTime < accessTokenExpired) {
            return;
        }

        PostMethod method = new PostMethod(url
                + "realms/" + realm + "/protocol/openid-connect/token");  
        if (currentTime < refreshTokenExpired) {
            method.setRequestBody(new NameValuePair[] {
                    new NameValuePair("grant_type", "refresh_token"),
                    new NameValuePair("refresh_token", refreshToken),
                    new NameValuePair("client_id", clientId),
                    new NameValuePair("client_secret", clientSecret),
                    new NameValuePair("scope", "openid")
            });
        } else {
            method.setRequestBody(new NameValuePair[] {
                    new NameValuePair("grant_type", "password"),
                    new NameValuePair("password", pw),
                    new NameValuePair("username", username),
                    new NameValuePair("client_id", clientId),
                    new NameValuePair("client_secret", clientSecret),
                    new NameValuePair("scope", "openid")
            });
        }
        
        if (proxyHost != null) {
            client.getHostConfiguration().setProxy(proxyHost, proxyPort);
        }
        client.executeMethod(method);
        JSONObject o = (JSONObject) HTTPUtils.json(method.getResponseBodyAsString());
        if (o.has("access_token")) {         
            accessToken = o.getString("access_token");
            accessTokenExpired = currentTime + o.getInt("expires_in");
            refreshToken = o.getString("refresh_token");
            refreshTokenExpired = currentTime + o.getInt("refresh_expires_in");
        } else {
            String error = "unknown";
            if (o.has("error_description")) {
                error = o.getString("error_description");
            }
            throw new IOException("error while getting keycloak access token: " + error);
        }
    }

    @Override
    public void setAuth(HttpClient client, HttpMethod method) throws IOException {
        getNewToken(client);
        method.addRequestHeader("Authorization", "bearer " + accessToken);
    }
    

}
